package main

import (
	"fmt"
)

// 切片 go语言特有的数据类型 数组的长度是不可变的，切片是建立在数组类型之上的抽象
// 切片（slice）是对数组一个连续片段的引用（该数组我们称之为相关数组，通常是匿名的），所以切片是一个引用类型
// 这个片段可以是整个数组，或者是由起始和终止索引标识的一些项的子集。需要注意的是，终止索引标识的项不包括在切片内。
// 切片是可索引的，并且可以由 len() 函数获取长度。
// ，切片的长度可以在运行时修改，最小为 0 最大为相关数组的长度：切片是一个 长度可变的数组。而数组是不可变的
//切片提供了计算容量的函数 cap() 可以测量切片最长可以达到多少：它等于切片从第一个元素开始，到相关数组末尾的元素个数。如果 s 是一个切片，cap(s) 就是从 s[0] 到数组末尾的数组长度。切片的长度永远不会超过它的容量，所以对于 切片 s 来说该不等式永远成立：0 <= len(s) <= cap(s)。

func main() {
	//定义一个数组
	var arr = [5]int{1, 2, 3, 4, 5}
	//定义切片 []里边不用写长度
	var slice = arr[0:5]
	//取出数组和切片的地址
	fmt.Printf("arr地址:%Pslice地址:%P\n", &arr, &slice)
	fmt.Println(slice)
	//修改切片的值
	slice[0] = 10
	//数组的值也跟着变化
	fmt.Println(slice, arr)
	//已经绑定数组的切片 试图扩容 失败
	//slice[6] = 6
	//fmt.Println(slice, arr)

	//cap可以输出切片的容量
	println("切片slice的容量为", cap(slice)) //最多扩容到数组的最后一项位置

	var arr1 [6]int        //定义一个没有赋值的数组
	var slice1 = arr1[2:5] //将数组下标为2的内存地址到下标为5(不包含5)的内存地址赋值给slice1
	for i := 0; i < len(arr1); i++ {
		arr1[i] = i //给数组进行赋值
	}

	//给数组赋值之后，输出切片
	for i := 0; i < len(slice1); i++ {
		fmt.Printf("第 %d个元素是%d\n", i, slice1[i])
	}

	fmt.Printf("arr1数组的长度为 %d\n", len(arr1))     //数组的大小不可变 就是一开始定义的6
	fmt.Printf("切片slice1的长度为%d\n", len(slice1))  //切片的大小是有 var slice1 []int = arr1[2:5] 决定，从数组的2到5不包含5，一共有3的元素
	fmt.Printf("切片slice1的容量为 %d\n", cap(slice1)) //因为数组最大下标为6，切片只能从2：5 扩容到2：len(arr)+1（将最后一位包含进去），因此切片可以扩容到的最大长度(容量)是数组的2到6，4个长度

	//扩容
	//从切片种切取下标为0开始的4个元素，但他原本只有三个，也就是0-3，那如果他的下一个地址空间还在数组包含中，那就将切片从数组的2-5改成2-6
	slice1 = slice1[0:4]
	for i := 0; i < len(slice1); i++ {
		fmt.Printf("第 %d个元素是%d\n", i, slice1[i])
	}

	//此时在输出切片的长度和容量
	fmt.Printf("slice1的长度为 %d\n", len(slice1))
	fmt.Printf("slice1的容量为 %d\n", cap(slice1)) //因为顶到数组最后一个内存地址了，无法在继续扩容了

	//如果 s2 是一个 slice，你可以将 s2 向后移动一位 s2 = s2[1:]，但是末尾没有移动。切片只能向后移动，s2 = s2[-1:] 会导致编译错误。切片不能被重新分片以获取数组的前一个元素。

	// 将切片传递给函数
	//如果你有一个函数需要对数组做操作，你可能总是需要把参数声明为切片。当你调用该函数时，把数组分片，创建为一个 切片引用并传递给该函数
	println(sum(arr1[:])) //arr1[2:] 和 arr1[2:len(arr1)] arr1[:3] 和 arr1[0:3] 相同 arr1[:]=arr[0:len(arr)]相同

	//用 make () 接受一个参数创建一个切片
	//当相关数组还没有定义时，我们可以使用 make () 函数来创建一个切片 同时创建好相关数组
	//格式var slice1 []type = make([]type, len) 也可以简写为 slice1 := make([]type, len)，这里 len 是数组的长度并且也是 slice 的初始长度。
	slice2 := make([]int, 3)
	println(cap(slice2)) //此时这里就是等于3
	//用make()接受两个参数创建一个切片
	//如果你想创建一个 slice1，它不占用整个数组，而只是占用以 len 为个数个项，那么只要：slice1 := make([]type, len, cap)
	slice3 := make([]int, 3, 5)
	println(len(slice3))
	println(cap(slice3))

	//new和make的区别
	//new (T) 为每个新的类型 T 分配一片内存，初始化为 0 并且返回类型为 * T 的内存地址：这种方法 返回一个指向类型为 T，值为 0 的地址的指针，它适用于值类型如数组和结构体；它相当于 &T{}。
	//make(T) 返回一个类型为 T 的初始值，它只适用于 3 种内建的引用类型：切片、map 和 channel。

	//[]byte 的切片十分常见，Go 语言有一个 bytes 包专门用来解决这种类型的操作方法
	//可以通过byte.buffer来创建一个与java中StringBuffer类似的对象

}

func sum(arr []int) int { //由于无法判断该函数需要操作的数组大小，因此将切片做为参数是很方便的
	s := 0
	for _, v := range arr {
		s = s + v
	}
	return s
}
